package com.smartstate.ems.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.extra.expression.ExpressionUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.smartstate.ems.dto.EnergyDTO;
import com.smartstate.ems.dto.PageDTO;
import com.smartstate.ems.entity.Energy;
import com.smartstate.ems.entity.HalfHourPowerMeterSummaryEntity;
import com.smartstate.ems.mapper.EnergyPanelMapper;
import com.smartstate.ems.mapper.PowerMeterSummaryMapper;
import com.smartstate.ems.service.EnergyPanelService;
import com.smartstate.ems.service.ObjMoudelService;
import com.smartstate.ems.util.MyExpressionUtil;
import com.smartstate.ems.util.PowerMeterSummaryUtils;
import com.smartstate.ems.vo.PowerVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

@Service
@Slf4j
public class EnergyPanelServiceImpl extends ServiceImpl<EnergyPanelMapper, Energy> implements EnergyPanelService {

    @Resource
    private EnergyPanelMapper energyPanelMapper;

    @Override
    public Page<Energy> queryAllPageByDTO(PageDTO pageDTO) {
        return null;
    }

    @Resource
    private ObjMoudelService objMoudelService;

    @Resource
    private PowerMeterSummaryMapper powerMeterSummaryMapper;
//
    /**
     * 能源看板
     * @return
     */
    @Override
    public List<Energy> queryEnergyData() {
        DateTime dateTime = new DateTime();
        String endDayStr = dateTime.offsetNew(DateField.DAY_OF_MONTH, -1).toDateStr();
        String startDayStr = dateTime.offsetNew(DateField.DAY_OF_MONTH, -14).toDateStr();
        int count = 0;
//        通过时间推移获取14天的峰，平，谷值
        List<Energy> energyList = new ArrayList<>();
        while(endDayStr.compareTo(startDayStr) >= 0){
            Energy energy = new Energy();
//            设置小数点不足两位添0格式
            DecimalFormat decimalFormat = new DecimalFormat(".00");
            String flatValueStr = energyPanelMapper.queryDayType(startDayStr,"flatValue");
            String peekValueStr = energyPanelMapper.queryDayType(startDayStr,"peekValue");
            String valleyValueStr = energyPanelMapper.queryDayType(startDayStr,"valleyValue");
            float flatValue = flatValueStr != null ? Float.valueOf(flatValueStr) : 0f;
            float peekValue = peekValueStr != null ? Float.valueOf(peekValueStr) : 0f;
            float valleyValue = valleyValueStr != null ? Float.valueOf(valleyValueStr) : 0f;
//            保留小数点两位，采用截断模式
            flatValue = new BigDecimal(flatValue).setScale(2, RoundingMode.DOWN).floatValue();
            peekValue = new BigDecimal(peekValue).setScale(2, RoundingMode.DOWN).floatValue();
            valleyValue = new BigDecimal(valleyValue).setScale(2, RoundingMode.DOWN).floatValue();
//          小数点不足两位添0
            flatValueStr = decimalFormat.format(flatValue);
            peekValueStr = decimalFormat.format(peekValue);
            valleyValueStr = decimalFormat.format(valleyValue);

            energy.setFlatValue(flatValueStr);
            energy.setPeekValue(peekValueStr);
            energy.setValleyValue(valleyValueStr);
            energy.setDateTime(startDayStr);
            float total = flatValue + peekValue + valleyValue;
            String totalStr = decimalFormat.format(total);
            energy.setTotal(totalStr);
            energyList.add(energy);
            count += 1;
            startDayStr = dateTime.offsetNew(DateField.DAY_OF_MONTH, -14 + count).toDateStr();
        }

        return energyList;
    }

    /**
     * 对象组能耗统计
     * @param energyDTO
     * @return
     */
    @Override
    public List<Map<String, Object>> queryObjectgroups(EnergyDTO energyDTO) {
        DateTime dateTime = new DateTime();
        DateTime endTime = DateUtil.parse(dateTime.toString(), "yyyy-MM-dd");
        endTime = DateUtil.offset(endTime, DateField.DAY_OF_WEEK, -1);
        DateTime startTime = DateUtil.offset(endTime, DateField.DAY_OF_WEEK, -6);
        energyDTO.setStartTime(startTime.toString());
        energyDTO.setEndTime(endTime.toString());
        List<Map<String, Object>> treeType = getTreeType(energyDTO);
//        设备还未配置返回null
        if (treeType == null || treeType.size() == 0){
            return null;
        }
//        若非叶节点无计算表达式，则将子集求和赋值到该节点
        treeType = treeType.stream().map((item) -> {
            item = assign(item);
            return item;
        }).collect(Collectors.toList());
//     当某天无数据，则生成一个map, num = -1给前端标记
        treeType = updata(treeType, endTime);
        return treeType;
    }

    /**
     * 获取每天的对象树
     * @param energyDTO
     * @return
     */
    public List<Map<String, Object>> getTreeType(EnergyDTO energyDTO){
        //遍历 拿到设备 电的计算规则
        List<Map<String, Object>> lisAllTree = new ArrayList<>();
        List<Map<String, String>> stringObjMoudelMap = objMoudelService.selectMetering(energyDTO);
        List<PowerVo> listTree = new ArrayList<>();

        List<PowerVo> listPower = new ArrayList<>();
        HashSet<String> hashSet = new HashSet();
        String endTimeStr = energyDTO.getEndTime();
        String startTimeStr = energyDTO.getStartTime();
        endTimeStr = endTimeStr.substring(0, 10);
        startTimeStr = startTimeStr.substring(0, 10);
        energyDTO.setEndTime(endTimeStr);
        energyDTO.setStartTime(startTimeStr);
        List<HalfHourPowerMeterSummaryEntity> hfList = powerMeterSummaryMapper.selectDatePrice(energyDTO);

        Map<String, List<HalfHourPowerMeterSummaryEntity>> hfMap = hfList.stream().collect(Collectors.groupingBy(HalfHourPowerMeterSummaryEntity::getMeterId));

        for (Map map : stringObjMoudelMap) {
            String id = (String) map.get("id");
            String parentId = (String) map.get("parentId");
            String name = (String) map.get("name");
            //拿到为电的计算公式
            if (map.get("电") != null && !"".equals(map.get("电"))) {
                String electricity = (String) map.get("电");
                energyDTO.setRateType("电");
                //格式化计算公式
                String s = MyExpressionUtil.quChu(electricity);
                //拿到计算公式里的所有deviceId

                List<String> expressionParams = MyExpressionUtil.getExpressionParams(electricity);


                List<HalfHourPowerMeterSummaryEntity> resultList = new ArrayList<>(8);
                expressionParams.forEach(e -> {
                    List<HalfHourPowerMeterSummaryEntity> deviceDataList = hfMap.get(e);
                    if (CollectionUtil.isNotEmpty(deviceDataList)){
                        resultList.addAll(deviceDataList);
                    }
                });

                Map<String, List<HalfHourPowerMeterSummaryEntity>> collect = new HashMap<>();
                if (energyDTO.getGraininess().equals("2")) {
                    collect = resultList.stream().
                            collect(Collectors.groupingBy(HalfHourPowerMeterSummaryEntity::getDayStr));
                    for (Map.Entry<String, List<HalfHourPowerMeterSummaryEntity>> entry : collect.entrySet()) {

                        List<HalfHourPowerMeterSummaryEntity> value = entry.getValue();
                        //树的根节点  第一层的所有时间段数据  一个时间段就代表一个树的根节点
                        HashMap<String, Object> mapWyf = new HashMap<>();
                        HashMap<String, Object> mapCxk = new HashMap<>();

                        HashMap<String, Object> mapLh = new HashMap<>();
                        HashMap<String, Object> mapYy = new HashMap<>();

                        //峰
                        HashMap<String,Object> mapPeekValue = new HashMap<>();
                        HashMap<String,Object> mapPeekValues = new HashMap<>();
                        //平
                        HashMap<String,Object> mapFlatValue = new HashMap<>();
                        HashMap<String,Object> mapFlatValues = new HashMap<>();
                        //谷
                        HashMap<String,Object> mapValleyValue = new HashMap<>();
                        HashMap<String,Object> mapValleyValues = new HashMap<>();

                        PowerVo powerVo = new PowerVo();
                        String time = "";
                        String rateType = "";

                        //把设备id 对应的能耗放进map  把时间和类型放到对象里
                        for (HalfHourPowerMeterSummaryEntity hs : value) {
                            //费用
                            mapLh.put(hs.getMeterId(),hs.getTotalPrice());
                            //能耗
                            mapWyf.put(hs.getMeterId(),hs.getDetActiveEnergy());
                            //峰
                            mapPeekValue.put(hs.getMeterId(),hs.getPeekValue());
                            //平
                            mapFlatValue.put(hs.getMeterId(),hs.getFlatValue());
                            //谷
                            mapValleyValue.put(hs.getMeterId(),hs.getValleyValue());


                            time = hs.getDayStr();
                            rateType = hs.getRateType();
                            hashSet.add(hs.getDayStr());

                        }
                        //把设备id拼接ll 对应能耗放进对应id里 有设备id的 没有能耗的放0
                        //[DHU0011,DHU0012]
                        //map{"DHU0012","123321"}
                        for (Object ls : expressionParams) {
                            for (Map.Entry<String, Object> stringObjectEntry : mapWyf.entrySet()) {
                                if (!ls.equals(stringObjectEntry.getKey())) {
                                    mapCxk.put("LL" + ls, 0);

                                }
                                if (ls.equals(stringObjectEntry.getKey())) {
                                    mapCxk.put("LL" + ls, stringObjectEntry.getValue());
                                    break;
                                }
                            }
                        }

                        //把设备id拼接ll 对应费率放进对应id里 有设备id的 没有费率的放0
                        //[DHU0011,DHU0012]
                        //map{"DHU0012","123321"}
                        for (Object ls : expressionParams) {
                            for (Map.Entry<String, Object> stringObjectEntry : mapLh.entrySet()) {
                                if (!ls.equals(stringObjectEntry.getKey())) {
                                    mapYy.put("LL" + ls, 0);

                                }
                                if (ls.equals(stringObjectEntry.getKey())) {
                                    mapYy.put("LL" + ls, stringObjectEntry.getValue());
                                    break;
                                }
                            }
                        }

                        //把设备id拼接ll 对应费率放进对应id里 有设备id的 没有费率的放0
                        //[DHU0011,DHU0012]
                        //map{"DHU0012","123321"}
                        //峰
                        for (Object ls : expressionParams) {
                                    for (Map.Entry<String, Object> stringObjectEntry : mapPeekValue.entrySet()) {
                                        if (!ls.equals(stringObjectEntry.getKey())) {
                                    mapPeekValues.put("LL" + ls, 0);

                                }
                                if (ls.equals(stringObjectEntry.getKey())) {
                                    mapPeekValues.put("LL" + ls, stringObjectEntry.getValue());
                                    break;
                                }
                            }
                        }
                        //把设备id拼接ll 对应费率放进对应id里 有设备id的 没有费率的放0
                        //[DHU0011,DHU0012]
                        //map{"DHU0012","123321"}
                        //平
                        for (Object ls : expressionParams) {
                            for (Map.Entry<String, Object> stringObjectEntry : mapFlatValue.entrySet()) {
                                if (!ls.equals(stringObjectEntry.getKey())) {
                                    mapFlatValues.put("LL" + ls, 0);

                                }
                                if (ls.equals(stringObjectEntry.getKey())) {
                                    mapFlatValues.put("LL" + ls, stringObjectEntry.getValue());
                                    break;
                                }
                            }
                        }

                        //把设备id拼接ll 对应费率放进对应id里 有设备id的 没有费率的放0
                        //[DHU0011,DHU0012]
                        //map{"DHU0012","123321"}
                        //谷
                        for (Object ls : expressionParams) {
                            for (Map.Entry<String, Object> stringObjectEntry : mapValleyValue.entrySet()) {
                                if (!ls.equals(stringObjectEntry.getKey())) {
                                    mapValleyValues.put("LL" + ls, 0);

                                }
                                if (ls.equals(stringObjectEntry.getKey())) {
                                    mapValleyValues.put("LL" + ls, stringObjectEntry.getValue());
                                    break;
                                }
                            }
                        }

//                        设置小数点不足两位添0格式
                        DecimalFormat decimalFormat = new DecimalFormat("0.00");
                        //{"llDHU0012+llDHU0011",{LLDHU0012:123},{LLDHU0011:0}}
                        //把上面的公式 和 map放进方法 算出能耗值
                        Float num = Float.parseFloat(ExpressionUtil.eval(s, mapCxk).toString());
                        //费率
                        Float price = Float.parseFloat(ExpressionUtil.eval(s, mapYy).toString());

                        //峰
                        Float PeekValue = Float.parseFloat(ExpressionUtil.eval(s, mapPeekValues).toString());
                        //平
                        Float FlatValue = Float.parseFloat(ExpressionUtil.eval(s, mapFlatValues).toString());
                        //谷
                        Float ValleyValue = Float.parseFloat(ExpressionUtil.eval(s, mapValleyValues).toString());
                        //平均价
                        Float rate =0.00F;
                        if (num>0 && price>0){
                            rate = (price/num);
                        }
                        //保留小数点两位，采用截断模式
                        num = new BigDecimal(num).setScale(2, RoundingMode.DOWN).floatValue();
                        PeekValue = new BigDecimal(PeekValue).setScale(2, RoundingMode.DOWN).floatValue();
                        FlatValue = new BigDecimal(FlatValue).setScale(2, RoundingMode.DOWN).floatValue();
                        ValleyValue = new BigDecimal(ValleyValue).setScale(2, RoundingMode.DOWN).floatValue();
                        price = new BigDecimal(price).setScale(2, RoundingMode.DOWN).floatValue();
                        rate = new BigDecimal(rate).setScale(2, RoundingMode.DOWN).floatValue();
                        //小数点不足两位添0
                        String numStr = decimalFormat.format(num);
                        String rates = decimalFormat.format(rate);
                        String prices = decimalFormat.format(price);
                        String ValleyValues = decimalFormat.format(ValleyValue);
                        String FlatValues = decimalFormat.format(FlatValue);
                        String PeekValues = decimalFormat.format(PeekValue);
                        //把所有需要的值全放入powerVo这个对象
                        powerVo.setRate(rates);
                        powerVo.setNum(numStr);
                        powerVo.setPrice(prices);
                        powerVo.setRateType(rateType);
                        powerVo.setId(id);
                        powerVo.setParentId(parentId);
                        powerVo.setName(name);
                        powerVo.setTime(time);

                        powerVo.setPeekValue(PeekValues);
                        powerVo.setFlatValue(FlatValues);
                        powerVo.setValleyValue(ValleyValues);
                        powerVo.setGraininess(energyDTO.getGraininess());

                        //我这个 listTree 已经是分好组的 数据 就是要遍历 这个 listTree 找出父子级关系 然后 比对时间  如果是父子级id 就比较
                        listTree.add(powerVo);
                    }
                }
            }else{
                PowerVo powerVo = new PowerVo();
                //把判断颗粒度时没有 计算公式的放到这个对象集合里
                powerVo.setId(id);
                powerVo.setParentId(parentId);
                powerVo.setName(name);
                powerVo.setGraininess(energyDTO.getGraininess());
                //我这个 listTree 已经是分好组的 数据 就是要遍历 这个 listTree 找出父子级关系 然后 比对时间  如果是父子级id 就比较
                listPower.add(powerVo);
            }
            if(listPower.size()>0){
                for (PowerVo powerVo:listPower) {
                    for (String setTime:hashSet) {
                        PowerVo powerVo1=  new PowerVo();
                        powerVo1.setId(powerVo.getId());
                        powerVo1.setParentId(powerVo.getParentId());
                        powerVo1.setGraininess(powerVo.getGraininess());
                        powerVo1.setRateType("");
                        powerVo1.setName(powerVo.getName());
                        powerVo1.setTime(setTime);
                        listTree.add(powerVo1);
                    }
                }
            }
        }
        Map<String, List<PowerVo>> collectPower = listTree.stream().collect(Collectors.groupingBy(PowerVo::getTime));

        PowerMeterSummaryUtils ps = new PowerMeterSummaryUtils();
        List<Map<String, Object>> time = ps.PowerTree(collectPower, energyDTO);
        lisAllTree = time.stream().sorted(Comparator
                        .comparing((Map<String, Object> map) ->
                                new DateTime(map.get("time").toString() + "-01")).reversed())
                .collect(Collectors.toList());
        return lisAllTree;
    }

    /**
     * 递归遍历map,当对象无表达式，num=0需要汇总子集的num进行赋值
     * @return
     */
    public Map<String, Object> assign(Map map){
        List<Map<String, Object>> mapList = (List<Map<String, Object>>) map.get("children");
        if (mapList.size() == 0){
            map.put("num", String.valueOf(map.get("num")).replace("(暂未配置计算公式)", ""));
            return map;
        }
        BigDecimal decimal = new BigDecimal("0.00");
        for (Map childrenMap : mapList) {
            Map<String, Object> objectMap = assign((Map) childrenMap);
            decimal = decimal.add(new BigDecimal(String.valueOf(objectMap.get("num")).replace("(暂未配置计算公式)", "")));
        }
//        没有公式自己进行赋值
        if(String.valueOf(map.get("num")).endsWith("(暂未配置计算公式)")){
            map.put("num", String.valueOf(decimal));
        }
        return map;
    }

    /**
     * 将树的num全设置为0
     * @return
     */
    public Map<String, Object> assignNum(Map map, String endTime){
        map.put("num", "0.00");
        map.put("time", endTime);
        List<Map<String, Object>> mapList = (List<Map<String, Object>>) map.get("children");
        List<Map<String, Object>> mapListCopy = new ArrayList<>();
        if (mapList.size() == 0){
            return map;
        }
        for (int i = 0; i < mapList.size(); i++){
//            深克隆map
            Map<String, Object> objectMapCopy = new HashMap<>();
            objectMapCopy.putAll(mapList.get(i));
            objectMapCopy = assignNum(objectMapCopy, endTime);
            mapListCopy.add(objectMapCopy);
        }
        map.put("children", mapListCopy);
        return map;
    }

    /**
     * 当某天无数据，则生成一个map, num = -1给前端标记
     * @param treeType
     * @param endTime
     * @return
     */
    public List<Map<String, Object>> updata(List<Map<String, Object>> treeType, DateTime endTime){
//        设置初始值
        AtomicInteger indexTree = new AtomicInteger(6);
        List<Map<Integer, Object>> indexList = new ArrayList<>();
//        获取时间对应树为空，并且获取构造的树应该插入treeType的下标
        for (int i = 0; i < treeType.size(); i++) {
            int index = indexTree.get();
            while(index >= 0){
                int count = i + indexList.size();
                DateTime endTimeUpdata = DateUtil.offset(endTime, DateField.DAY_OF_WEEK, -6 + index);
                String endTimeStr = DateUtil.format(endTimeUpdata, "yyyy-MM-dd");
                if (!treeType.get(i).get("time").equals(endTimeStr)){
                    Map<Integer, Object> objectHashMap = new HashMap<>();
                    objectHashMap.put(count, endTimeStr);
                    indexList.add(objectHashMap);
                    index --;
                }else {
                    index --;
                    break;
                }
                count ++;
            }
            indexTree.set(index);
        }
//        当只查到当前时间的前n天(n<=5)，需要将前面的7-n天补齐
        int size = 7 - indexList.size() - treeType.size();
        for(int i = size; i > 0; i--){
            Map<Integer, Object> objectHashMap = new HashMap<>();
            DateTime endTimeUpdata = DateUtil.offset(endTime, DateField.DAY_OF_WEEK, -6 + i - 1);
            String endTimeStr = DateUtil.format(endTimeUpdata, "yyyy-MM-dd");
            objectHashMap.put(treeType.size() + i, endTimeStr);
            indexList.add(objectHashMap);
        }
//        获取时间对应树为空，并且根据获取treeType的下标添加数据
        for (Map<Integer, Object> objectMap : indexList) {
            objectMap.keySet().forEach((key) -> {
                Map<String, Object> hashMap = new HashMap<>();
                hashMap.putAll(treeType.get(0));
//                将树的num全设置为0
                hashMap = assignNum(hashMap, objectMap.get(key).toString());
                if (key < treeType.size()){
                    treeType.add(key, hashMap);
                }else {
                    treeType.add(hashMap);
                }
            });
        }
        return treeType;
    }
}
